package fileutils

import (
	"bufio"
	"bytes"
	"errors"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io"
	"io/fs"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	WindowsSeparator = '\\'
	UnixSeparator    = '/'
)

// IsWindows 返回当前环境是否是Windows
func IsWindows() bool {
	return WindowsSeparator == filepath.Separator
}

// IsUnix 返回当前环境是否是Unit
func IsUnix() bool {
	return UnixSeparator == filepath.Separator
}

func GetLineSeparator() string {
	goos := runtime.GOOS
	if goos == "windows" {
		return "\n\r"
	} else if goos == "linux" {
		return "\n"
	} else if goos == "darwin" {
		return "\r"
	} else {
		return "\n"
	}
}

// GetFileSeparator 获取文件分割符，如：/ \
func GetFileSeparator() string {
	return string(filepath.Separator)
}

// IsEmpty 返回当前 filepath 是否为空:
//
// 	1. 若filepath为目录，则判断目录下是否存在文件或文件夹
// 	2. 若filepath为文件，则判断此文件大小是否为0
func IsEmpty(file string) bool {
	stat, err := os.Stat(file)
	if err != nil {
		return true
	}

	if stat.IsDir() {
		dir, err := ioutil.ReadDir(file)
		return err == nil && len(dir) == 0
	} else {
		return stat.Size() == 0
	}
}

// IsNotEmpty 返回IsEmpty相反值
func IsNotEmpty(file string) bool {
	return !IsEmpty(file)
}

// IsDirEmpty 返回dir目录是否为空，若dir不是目录，抛出error
func IsDirEmpty(dir string) (isEmpty bool, err error) {
	stat, err := os.Stat(dir)
	if err != nil {
		return false, err
	} else {
		if stat.IsDir() {
			dirs, err := ioutil.ReadDir(dir)
			return len(dirs) == 0, err
		} else {
			return false, errors.New(dir + " 不是一个目录")
		}
	}
}

// Exists 返回文件或目录是否存在
func Exists(file string) bool {
	_, err := os.Stat(file)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}

	return true
}

// IsDir 返回是否是目录
func IsDir(file string) (bool, error) {
	stat, err := os.Stat(file)
	if err != nil {
		return false, err
	}

	return stat.IsDir(), nil
}

// IsFile 返回 IsDir 相反值
func IsFile(file string) (bool, error) {
	stat, err := os.Stat(file)
	if err != nil {
		return false, err
	}

	return !stat.IsDir(), nil
}

// ListFiles 列出file下的文件
func ListFiles(file string) ([]fs.FileInfo, error) {
	dir, err := ioutil.ReadDir(file)
	if err != nil {
		return nil, err
	} else {
		return dir, nil
	}
}

// GetFile 获取文件，且文件未定义关闭
func GetFile(file string) (*os.File, error) {
	isFile, err := IsFile(file)
	if err != nil {
		return nil, err
	}

	if !isFile {
		return nil, errors.New(file + "不是一个文件")
	}

	return os.OpenFile(file, os.O_RDWR, fs.ModePerm)
}

// GetFileName 获取文件名
func GetFileName(file string) string {
	return filepath.Base(file)
}

// GetParent 获取上级目录
func GetParent(file string) string {
	return GetSupper(file, 1)
}

// GetSupper 获取上级第level级目录
func GetSupper(file string, level int) string {
	abs, err := filepath.Abs(file)
	if err != nil || level < 1 {
		return abs
	}

	return GetSupper(filepath.Dir(abs), level-1)
}

// Abs 返回文件绝对路径
func Abs(file string) string {
	abs, _ := filepath.Abs(file)
	return abs
}

// GetPrefix 获取文件名，不带扩展名
func GetPrefix(file string) string {
	fileName := GetFileName(file)
	index := strings.LastIndexAny(fileName, ".")
	if index == -1 {
		index = len(fileName)
	}
	return fileName[:index]
}

// GetSuffix 获取扩展名，不带.和文件名
func GetSuffix(file string) string {
	fileName := GetFileName(file)
	index := strings.LastIndexAny(fileName, ".")
	if index == -1 {
		index = len(fileName) - 1
	}
	return fileName[index+1:]
}

// GetUserHomeDir 获取用户目录
func GetUserHomeDir() (string, error) {
	return os.UserHomeDir()
}

// Chmod 更改文件权限
func Chmod(name string, mode os.FileMode) error {
	return os.Chmod(name, mode)
}

// MkDirAll 创建所有路径
func MkDirAll(path string) error {
	return os.MkdirAll(path, fs.ModePerm)
}

// CreateFile 创建文件以及对应的目录，若存在则直接返回文件
func CreateFile(file string) (*os.File, error) {
	if Exists(file) {
		return nil, os.ErrExist
	} else {
		err := os.MkdirAll(GetParent(file), 0766)
		if err != nil {
			return nil, err
		}

		f, err := os.Create(file)
		if err != nil {
			return nil, err
		}
		return f, nil
	}
}

// Copy 复制文件
func Copy(src, dest string) error {
	srcFile, err := GetFile(src)
	if err != nil {
		return err
	}
	defer CloseFile(srcFile)

	destFile, err := CreateFile(dest)
	if err != nil {
		return err
	}
	defer CloseFile(destFile)

	l, err := io.Copy(destFile, srcFile)
	if err != nil {
		return err
	}

	state, err := FileState(src)
	if err != nil {
		return err
	}

	if l != state.Size() {
		return errors.New("复制失败，文件大小不正确")
	}

	return destFile.Sync()
}

// GetTempDir 获取系统临时文件路径
func GetTempDir() string {
	return os.TempDir()
}

// CreateRandomTempDir 创建随机名称的文件夹
func CreateRandomTempDir() (string, error) {
	return ioutil.TempDir("", "")
}

// CreateTempDir 按模式创建临时文件夹，如 test* ->test3267258504
func CreateTempDir(prefix string) (string, error) {
	return ioutil.TempDir("", prefix)
}

// CreateTempFile 根据文件名模板，创建临时文件，如test*.go -> test2669598681.go
func CreateTempFile(tmpFile string) (*os.File, error) {
	return ioutil.TempFile("", tmpFile)
}

// Rename 重命名
func Rename(file string, newName string) error {
	return os.Rename(file, newName)
}

// Remove 删除文件
func Remove(file string) error {
	return os.Remove(file)
}

// RemoveAll 删除所有文件
func RemoveAll(path string) error {
	return os.RemoveAll(path)
}

// ReadGBK 读取文件内容并转换为GBK
func ReadGBK(file string) (string, error) {
	gbkBytes, err := ReadGBKBytes(file)
	if err != nil {
		return "", err
	}

	return string(gbkBytes), nil
}

// FileState 获取文件状态
func FileState(file string) (os.FileInfo, error) {
	return os.Stat(file)
}

// Read 读取文件内容为字符串，不处理编码
func Read(file string) (string, error) {
	fileBytes, err := ReadBytes(file)
	if err != nil {
		return "", err
	}

	return string(fileBytes), nil
}

// ReadGBKBytes 读取文件内容并转换为GBK
func ReadGBKBytes(file string) ([]byte, error) {
	bs, err := ReadBytes(file)
	if err != nil {
		return nil, err
	}

	reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder())
	return ioutil.ReadAll(reader)
}

// ReadBytes 读取文件内容为字符串，不处理编码
func ReadBytes(file string) ([]byte, error) {
	return ioutil.ReadFile(file)
}

// ReadLines 按行读取文件
func ReadLines(file string) ([]string, error) {
	fileBytes, err := ReadBytes(file)
	if err != nil {
		return nil, err
	}
	reader := bytes.NewReader(fileBytes)

	scanner := bufio.NewScanner(reader)
	var lines []string
	for scanner.Scan() {
		line := scanner.Text()
		lines = append(lines, line)
	}

	return lines, scanner.Err()
}

// ReadGBKLines 按行读取文件
func ReadGBKLines(file string) ([]string, error) {
	gbkBytes, err := ReadGBKBytes(file)
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(bytes.NewReader(gbkBytes))
	var lines []string
	for scanner.Scan() {
		line := scanner.Text()
		lines = append(lines, line)
	}

	return lines, scanner.Err()
}

// AppendString 追加字符串
func AppendString(file string, content string) error {
	f, err := GetFile(file)
	if err != nil {
		return err
	}

	defer CloseFile(f)

	n, _ := f.Seek(0, io.SeekEnd)
	_, err = f.WriteAt([]byte(content), n)
	return err
}

// AppendGBKString 追加GBK字符串
func AppendGBKString(file string, content string) error {
	f, err := GetFile(file)
	if err != nil {
		return err
	}

	defer CloseFile(f)

	reader := transform.NewReader(bytes.NewReader([]byte(content)), simplifiedchinese.GBK.NewEncoder())
	bs, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}

	n, _ := f.Seek(0, io.SeekEnd)
	_, err = f.WriteAt(bs, n)
	return err
}

// AppendLine 添加一行文本
func AppendLine(file string, line string) error {
	return AppendString(file, GetLineSeparator()+line)
}

// AppendLines 添加多行文本
func AppendLines(file string, lines []string) error {
	if len(lines) > 0 {
		f, err := GetFile(file)
		if err != nil {
			return err
		}

		defer CloseFile(f)
		separator := GetLineSeparator()

		for _, line := range lines {
			seek, err := f.Seek(0, io.SeekEnd)
			if err != nil {
				return err
			}

			_, err = f.WriteAt([]byte(separator+line), seek)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// AppendGBKLines 添加GBK多行文本
func AppendGBKLines(file string, lines []string) error {
	if len(lines) > 0 {
		f, err := GetFile(file)
		if err != nil {
			return err
		}

		defer CloseFile(f)

		separator := GetLineSeparator()

		for _, line := range lines {
			seek, err := f.Seek(0, io.SeekEnd)
			if err != nil {
				return err
			}

			reader := transform.NewReader(bytes.NewReader([]byte(separator+line)), simplifiedchinese.GBK.NewEncoder())
			bs, err := ioutil.ReadAll(reader)
			if err != nil {
				return err
			}

			_, err = f.WriteAt(bs, seek)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// Write 将内容写入文本
func Write(file string, bs []byte) error {
	return ioutil.WriteFile(file, bs, fs.ModePerm)
}

// WriteString 将文本写入文件
func WriteString(file, content string) error {
	return Write(file, []byte(content))
}

// CloseFile 关闭文件
func CloseFile(f *os.File) {
	err := f.Close()
	if err != nil {
		log.Fatal(err)
	}
}
